#include "access/DatabaseManager.h"

#include <iostream>

DatabaseManager::DatabaseManager(BufferManager* man)
{
    dman_mutex = PTHREAD_MUTEX_INITIALIZER;//
    
    bm = man;
}

bool DatabaseManager::AddDatabase(const char* name, uint8_t column_count, KeyType type, Transaction* trans)
{
    // check if the transaction is already holding the mutex

    void* copyLink = trans->FindLink((void*)this);

	//cout << copyLink << endl;
    
	DatabaseManager* copyDM = (DatabaseManager*)copyLink;
    
    pthread_mutex_t* cur_mutex;
    string db_name = name;
    
    // if the transaction doesn't hold the lock, we need to obtain the lock first
    if(!copyDM)
    {
        // lock the database manager first
        // there are two cases
        // 1) if the database already exists, the lock is released before the function returns
        // 2) if the database doesn't exist, the lock is released by the Transaction by commit or abort
        cur_mutex = LockPage();
    
        map<string, Database*>::iterator it;
    
        // search the database name in the database array
        it = dbArray.find(db_name);
    
        if(it != dbArray.end())
        {
            //cout << "Datbase already exists : " << db_name << endl;
        
            pthread_mutex_unlock(cur_mutex);
        
            return false;
        }

    
        // duplicate the current database manager
        copyDM = new DatabaseManager(*this);
    
        // duplicate the database sequence
        copyDM->dbArray = this->dbArray;
	    
		// add it into the transaction sequence
		trans->AddDBManager(cur_mutex, this, copyDM);

    }
    
    
    Database* db = new Database(name, column_count, type, bm);

    copyDM->dbArray.insert(pair<string, Database*>(db_name, db));

	// we also lock the new database (unnecessarily?)
	// and add it into the trans manager
	// note that there's no original db involved
	pthread_mutex_t* db_mutex = db->LockPage();

	trans->AddDatabase(db_mutex, NULL, (void*)db);

	return true;
}

DatabaseManager::~DatabaseManager()
{
    /*
    map<string, Database*>::iterator it;
    
    for( it = dbArray.begin(); it != dbArray.end(); it++)
    {
        cout << "Deleting database : " << it->first << endl;
        Database* db = it->second;
        
        delete db;
    }
    */
}

void DatabaseManager::PrintDatabase(void* db_pointer)
{
    /*
    map<string, Database*>::iterator it;
    
    for( it = dbArray.begin(); it != dbArray.end(); it++)
    {
        Database* db = it->second;

        db->ReportInfo();
    }
    */
    Database* db = (Database*)db_pointer;
    
    db->ReportInfo();
    
}

void DatabaseManager::CopyDatabaseManager(DatabaseManager* dest, DatabaseManager* source)
{
    // remember the only difference between the DB managers is the database array
    dest->dbArray = source->dbArray;
}

pthread_mutex_t* DatabaseManager::LockPage()
{
    pthread_mutex_lock(&dman_mutex);
    
    return &dman_mutex;
}

bool DatabaseManager::InsertRecord(void *db_pointer, Record *record, Transaction* trans)
{
    Database* db = (Database*)db_pointer;
    
    return db->InsertRecord(record, trans);
    
}


Database* DatabaseManager::OpenDatabase(const char* name)
{
	/*
    void* copyLink = trans->FindLink((void*)this);
    
    DatabaseManager* curDM = (DatabaseManager*)copyLink;
    
    pthread_mutex_t* cur_mutex;
    
    if(!curDM)
    {
        curDM = this;
    
        cur_mutex = LockPage();
    }
    */

	DatabaseManager* curDM = this;

	pthread_mutex_t* cur_mutex = LockPage();

    string db_name = name;
    map<string, Database*>::iterator it;
    
    // search the database name in the database array
    it = curDM->dbArray.find(db_name);
    
    if(it == curDM->dbArray.end())
    {
        //cout << "Datbase doesn't exists : " << db_name << endl;
        
        pthread_mutex_unlock(cur_mutex);
        
        return NULL;
    }

    //trans->OpenDatabase((void*)it->second);
    
    pthread_mutex_unlock(cur_mutex);
    
    return it->second;
}

// in our implementation, we don't need to do any for database closure
bool DatabaseManager::CloseDatabase(Database* d)
{
	return true;
}

